package main

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"errors"
	"fmt"
	"log"
	"net/url"
	"strconv"

	"github.com/gorilla/websocket"
)

var wsConnection *websocket.Conn = nil

// notSentQueue 因网络问题或者其他问题没有发送到中心服务器的数据队列
var notSentQueue *list.List = list.New()

func initWebsocket() error {
	u := url.URL{
		Scheme:   "ws",
		Host:     fmt.Sprintf("%s:%d", conf.CentralServerIP, conf.CentralServerPort),
		Path:     "/dacData/" + conf.Name,
		RawQuery: "port=" + strconv.Itoa(conf.WebServerPort) + "&samplesPerSecond=" + strconv.Itoa(conf.SamplesPerSecond) + "&unit=" + conf.Unit,
	}
	log.Printf("connecting to %s\n", u.String())

	var err error
	wsConnection, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Println("dial error: ", err)
		if err == websocket.ErrBadHandshake {
			return errors.New("连接数据中心服务器失败，请是否已经存在连接，或者采集卡名称是否出现重复")
		}
		return err
	}

	return nil
}

func closeWebsocket() {
	if wsConnection != nil {
		wsConnection.Close()
		wsConnection = nil
	}
}

// sendOneSample 把一个样本点数据发送到数据中心服务器
func sendOneSample(sampleData sampleDataType) error {
	if wsConnection == nil {
		if err := initWebsocket(); err != nil {
			log.Printf("初始化数据中心服务器连接失败, 错误: %v", err)
			return err
		}
	}
	binaryDataBuffer := new(bytes.Buffer)
	err := binary.Write(binaryDataBuffer, binary.LittleEndian, sampleData)
	if err != nil {
		log.Println("binary.Write failed in sendDataToCentralServer: ", err)
		return err
	}
	// tryTimes 已经尝试的次数
	tryTimes := 1
	// retryLimit 重试次数
	const retryLimit int = 10
	for tryTimes <= retryLimit {
		err := wsConnection.WriteMessage(websocket.BinaryMessage, binaryDataBuffer.Bytes())
		if err != nil {
			log.Println("failed to write to websocket: ", err)
		} else {
			break
		}
		tryTimes++
	}
	if tryTimes > retryLimit {
		// 超过重试次数，发送失败
		return errors.New("failed to send to central server")
	}
	return nil
}

// sendDataToCentralServer 发送样本点数据（包括之前未发送成功的）到中心服务器
func sendDataToCentralServer(sampleData sampleDataType) error {
	for notSentQueue.Len() > 0 {
		// 存在未发送成功的数据，先发送这些
		e := notSentQueue.Front()
		err := sendOneSample(e.Value.(sampleDataType))
		if err != nil {
			// 还是失败，重连服务器，下次再发送，并将新的数据点放入notSentQueue，保证发送顺序
			closeWebsocket()
			notSentQueue.PushBack(sampleData)
			return err
		}
		notSentQueue.Remove(e)
	}

	if err := sendOneSample(sampleData); err != nil {
		closeWebsocket()
		notSentQueue.PushBack(sampleData)
		return err
	}
	return nil
}
